using System;
using System.IO;
using System.Net;
using System.Text;
using System.Web.Services.Description;
using System.Xml;
using System.Globalization;
using System.Security.Authentication;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace Thinktecture.Tools.Web.Services.CodeGeneration
{
	/// <summary>
	/// Summary description for WsdlHelper.
	/// </summary>
	public sealed class WsdlHelper
	{
		private WsdlHelper() {}

		public static string GetWsdlFromFile(string fileFullPathName)
		{
			FileInfo fi = null;

			try
			{
				fi = new FileInfo(fileFullPathName);
			}
			catch(Exception ex)
			{
				throw new WsdlProcessingException("This is not a valid WSDL file.", ex);
			}
					
			if(fi.Extension == ".wsdl")
			{
				FileStream fs = new FileStream(fileFullPathName, FileMode.Open, FileAccess.Read);
				StreamReader sr = new StreamReader(fs);               

				char[] buffer = new char[(int)fs.Length];
				sr.ReadBlock(buffer, 0, (int)fs.Length);
				sr.Close();

				string ws = new string(buffer);
                RemoveInvalidCharsFromWsdl(ref ws);
                return ws;
			}
				
			throw new WsdlProcessingException("This is not a valid WSDL file.");
		}

        public static bool RequireSslCertificate(Uri uri)
        {
            try
            {
                WebRequest req = WebRequest.Create(uri);
                WebResponse result = req.GetResponse();
            }
            catch (WebException wex)
            {
                if (wex.Status == WebExceptionStatus.TrustFailure)
                {
                    return true;
                }
            }
            catch
            {                
            }

            return false;
        }

		public static string GetWsdlFromUri(Uri uri)
		{
            ServicePointManager.ServerCertificateValidationCallback =
                new RemoteCertificateValidationCallback(CertValidation);

			WebRequest req = WebRequest.Create(uri);            
			WebResponse result = req.GetResponse();
					
			Stream ReceiveStream = result.GetResponseStream();
			Encoding encode = Encoding.GetEncoding("utf-8");
			StreamReader sr = new StreamReader(ReceiveStream, encode);
					
			string wsdlSource = sr.ReadToEnd();
			sr.Close();
            RemoveInvalidCharsFromWsdl(ref wsdlSource);		
			return wsdlSource;
		}

        public static bool CertValidation(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {
            return true;
        }

		public static string SaveWsdlFileLocally(string filename, string wsdl)
		{
			// BDS: Fixed a bug here. We did not check whether a period exisits in the 
			// filename. Threfore this code crashed when we have something without a 
			// period as filename.
			if(filename.LastIndexOf('.') > -1 )
			{
				filename = filename.Substring(0, filename.LastIndexOf('.')) + ".wsdl";
			}
			else
			{
				filename = filename + ".wsdl";
			}

			StreamWriter sw = new StreamWriter(filename);
			sw.WriteLine(wsdl);
			sw.Flush();
			sw.Close();

			return filename;
		}

		public static string GetLocalWsdlFilename(Uri wsdlUri, string wsdlContents)
		{
			string lastSeg = wsdlUri.Segments[wsdlUri.Segments.Length-1];
			if(lastSeg.IndexOf('.') > -1) return lastSeg;

			StringReader  wsdlStringReader = new StringReader(wsdlContents);
			XmlTextReader xr = new XmlTextReader(wsdlStringReader);                     
			ServiceDescription sd = ServiceDescription.Read(xr);
			
			// BDS: Fixed a bug here. We had tried to read the service name from the 
			// service description only. But some WSDL files have the service name either 
			// in the <service> element or in the <binding> element.
			if(sd.Name != null && sd.Name != "")
			{
				return sd.Name;
			}
			else if(sd.Services.Count > 0 && sd.Services[0].Name != null && 
				sd.Services[0].Name != "")
			{
				return sd.Services[0].Name;
			}
			else
			{
				return sd.Bindings[0].Name;
			}
		}	
		
		public static bool HasRpcEncodedBindings(string wsdl)
		{            
			XmlDocument doc = new XmlDocument();            
			doc.LoadXml(wsdl);
			
			XmlNamespaceManager nsMgr = 
				new XmlNamespaceManager(new NameTable());
			
			// Insert the namespaces required for the query to the namespace manager.
			nsMgr.AddNamespace("wsdl", "http://schemas.xmlsoap.org/wsdl/");
			nsMgr.AddNamespace("soap", "http://schemas.xmlsoap.org/wsdl/soap/");

			XmlNodeList list = 
				doc.SelectNodes("/wsdl:definitions/wsdl:binding[(soap:binding/@style='rpc') and (./wsdl:operation/*/soap:body/@use='encoded')]", 
				nsMgr); 
			return(list.Count > 0);
		}

		public static string GetWsdlName(string wsdl)
		{
			string name = "";
			ServiceDescription srvDesc = null;
			StringReader sr = new StringReader(wsdl);
			try
			{				
				srvDesc = ServiceDescription.Read(sr);
			}
			catch(Exception ex)
			{
				throw ex;
			}
			finally
			{
				sr.Close();
			}
			if(srvDesc.Name != null && srvDesc.Name != "")
			{
				name = srvDesc.Name;
			}
			else if(srvDesc.Services.Count > 0)
			{
				name = srvDesc.Services[0].Name;
			}
			else if(srvDesc.Bindings.Count > 0)
			{
				name = srvDesc.Bindings[0].Name;
			}
			
			return name;
		}	

		public static bool IsDocLitWrapMode(string wsdl)
		{
			ServiceDescription srvDesc = null;
			StringReader sr = new StringReader(wsdl);
			try
			{
				srvDesc = ServiceDescription.Read(sr);
			}
			catch(Exception ex)
			{
				throw ex;
			}
			finally
			{
				sr.Close();
			}

			foreach(Message message in srvDesc.Messages)
			{
				if(message.Parts.Count == 1)
				{
					if(string.Compare(message.Parts[0].Name, 
						"parameters", false, CultureInfo.InvariantCulture) == 0)
					{
						return true;
					}
				}
			}

			return false;
		}

		public static string GetWsdlNameFromLocalFile(string wsdlFile)
		{
			string source = "";
			StreamReader sr = null;
			try
			{
				sr = new StreamReader(wsdlFile);
				source = sr.ReadToEnd();
			}
			catch(Exception ex)
			{
				throw ex;
			}
			finally
			{
				if(sr != null)
				{
					sr.Close();
				}
			}

			return GetWsdlName(source);
		}

        private static void RemoveInvalidCharsFromWsdl(ref string wsdl)
        {
            // Check the starting char.
            if (!wsdl.StartsWith("<"))
            {
                wsdl = wsdl.Substring(wsdl.IndexOf("<"));
            }

            if (!wsdl.EndsWith(">"))
            {
                wsdl = wsdl.Substring(0, wsdl.LastIndexOf(">") + 1);
            }
        }
	}
}
